gtkmain: Look up transient-for hierarchies to determine modality
authorCarlos Garnacho <carlosg@gnome.org>
Mon, 22 Jun 2020 13:31:36 +0000 (15:31 +0200)
committerCarlos Garnacho <carlosg@gnome.org>
Tue, 23 Jun 2020 21:42:53 +0000 (23:42 +0200)
Windows that are not modal, but are transient-for a modal window should
still be able to receive and handle events. Inspect the window hierarchy
in those cases, so these windows are handled just like widgets within
the modal dialog.

Fixes: https://gitlab.gnome.org/GNOME/gtk/-/issues/2851
Related: https://gitlab.gnome.org/GNOME/gtk/-/issues/2850

gtk/gtkmain.c

index 84e7efdc8d382490f9a2bfc6fe69f4fb6c4fa8fe..fd43c9eb778b4323a06a335fa1a9e02b7d6176f5 100644 (file)
@@ -1663,6 +1663,25 @@ handle_key_event (GdkEvent *event)
   return focus_widget ? focus_widget : event_widget;
 }
 
+static gboolean
+is_transient_for (GtkWindow *child,
+                  GtkWindow *parent)
+{
+  GtkWindow *transient_for;
+
+  transient_for = gtk_window_get_transient_for (child);
+
+  while (transient_for)
+    {
+      if (transient_for == parent)
+        return TRUE;
+
+      transient_for = gtk_window_get_transient_for (transient_for);
+    }
+
+  return FALSE;
+}
+
 void
 gtk_main_do_event (GdkEvent *event)
 {
@@ -1726,11 +1745,16 @@ gtk_main_do_event (GdkEvent *event)
 
   /* If the grab widget is an ancestor of the event widget
    * then we send the event to the original event widget.
-   * This is the key to implementing modality.
+   * This is the key to implementing modality. This also applies
+   * across windows that are directly or indirectly transient-for
+   * the modal one.
    */
   if (!grab_widget ||
       ((gtk_widget_is_sensitive (target_widget) || gdk_event_get_event_type (event) == GDK_SCROLL) &&
-       gtk_widget_is_ancestor (target_widget, grab_widget)))
+       gtk_widget_is_ancestor (target_widget, grab_widget)) ||
+      (GTK_IS_WINDOW (grab_widget) &&
+       grab_widget != event_widget &&
+       is_transient_for (GTK_WINDOW (event_widget), GTK_WINDOW (grab_widget))))
     grab_widget = target_widget;
 
   g_object_ref (target_widget);